package com.wsp.service.service;

import com.wsp.apimanagement.pojo.dto.Result;
import com.wsp.apimanagement.pojo.entity.Comment;
import com.wsp.apimanagement.pojo.entity.Like;
import com.wsp.apimanagement.pojo.entity.Record;
import com.wsp.apimanagement.pojo.vo.CommentInfo;
import com.wsp.apimanagement.pojo.vo.ModuleRecord;
import com.wsp.apimanagement.pojo.vo.RecordInfo;
import com.wsp.apimanagement.pojo.vo.UserCommentInfo;
import com.wsp.apimanagement.service.RecordService;
import com.wsp.apimanagement.util.CommonResultBuilderUtil;
import com.wsp.service.lock.LikeReadWriteLock;
import com.wsp.service.util.LockKeepLiveUtil;
import com.wsp.service.util.RedisUtil;
import com.wsp.service.util.QiNiuCdnUtil;
import com.wsp.service.mapper.RecordMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Service;

import java.io.BufferedInputStream;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Package: com.wsp.service.service
 * Description： TODO
 *
 * @Author: 巫思平
 * Copyright: Copyright (c) 2020
 * Version: 1
 */
@Service
@ConfigurationProperties("wsp.redis.like")
public class RecordServiceImpl implements RecordService {
    //说说用户点赞数和点赞的用户id信息 string--》zset
    private static String recordTotalLikeNum;
    private static String recordUserLikeList;
    //新点赞 zset--》hash
    private static String newUserRecord;
    private static String newUserLikeList;
    //取消点赞 zset--》hash
    private static String deleteUserRecord;
    private static String deleteUserLikeList;
    //进入点赞的用户id（控制并发避免重复操作）
    private static String changeLikeUserList;

    @Autowired
    private RecordMapper recordMapper;
    @Autowired
    private QiNiuCdnUtil cdnUtil;
    @Autowired
    private RedisUtil redisUtil;

    private static final Object lock = new Object();

    //核心
    @Override
    public Result<Integer> likeRecord(String userId, String recordId) {
        try {
            //读写锁，避免更新时出现并发问题
            if(LikeReadWriteLock.readLock.tryLock(100,TimeUnit.MILLISECONDS)) {
                //redis控制用户操作,避免重复操作
                if(!redisUtil.setNotExit(changeLikeUserList+userId+recordId,recordId)) {
                    return Result.<Integer>builder().code(400).msg("不要重复操作").build();
                } else {
                    LockKeepLiveUtil.LIVE_TASK.put(Thread.currentThread(),changeLikeUserList+userId+recordId);
                    try {
                        //同步加锁--》两段锁+数据预热+定时任务队列+数据更新
                        List<Like> likeList;
                        Set<Object> userNum = redisUtil.zGet(recordUserLikeList+recordId);
                        //双重校验锁
                        if (userNum.size()==0) {
                            synchronized (lock) {
                                //显然这里如果也为空，说明要么数据不在缓存中要么数据本身为0，获取到锁的需要到数据库中查询后放入redis
                                userNum = redisUtil.zGet(recordUserLikeList+recordId);
                                //再次校验是否获取到，没有说明需要到数据库中获取
                                if (userNum.size()==0) {
                                    likeList = recordMapper.getRecordTotalLike(Long.parseLong(recordId));
                                    redisUtil.set(recordTotalLikeNum+recordId, likeList.size());
                                    for (Like like : likeList) {
                                        redisUtil.zadd(recordUserLikeList+recordId, like.getUserId()+"", like.getGmtCreate().getTime());
                                    }
                                } else {
                                    //说明数据已经预热，只需要更新redis数据库数据即可
                                    redisUtil.set(recordTotalLikeNum+recordId, userNum.size());
                                }
                            }
                        }
                        //判断是否点过赞，存在则为取消点赞；后面通过模拟数据库事件实现；俩个undo
                        if (userNum.contains(userId)) {
                            redisUtil.zRemove(recordUserLikeList+recordId, userId);
                            //数据库快照是否存在该数据，存在直接回滚
                            if (redisUtil.hget(newUserLikeList+recordId, userId)!=null) {
                                redisUtil.zIncrememt(newUserRecord, recordId,-1);
                                redisUtil.hdel(newUserLikeList+recordId, userId);
                            } else {
                                //加入待处理打卡记录队列
                                redisUtil.zIncrememt(deleteUserRecord, recordId,1);
                                redisUtil.hset(deleteUserLikeList+recordId, userId, System.currentTimeMillis());
                            }
                            //总点赞数-1
                            redisUtil.decr(recordTotalLikeNum+recordId, 1);
                        } else {
                            redisUtil.zadd(recordUserLikeList+recordId, userId, System.currentTimeMillis());
                            //数据库快照是否存在该数据，存在直接回滚
                            if (redisUtil.hget(deleteUserLikeList+recordId, userId)!=null) {
                                redisUtil.zIncrememt(deleteUserRecord, recordId,-1);
                                redisUtil.hdel(deleteUserLikeList+recordId, userId);
                            } else {
                                //加入待处理点赞队列
                                redisUtil.zIncrememt(newUserRecord, recordId,1);
                                redisUtil.hset(newUserLikeList+recordId, userId, System.currentTimeMillis());
                            }
                            redisUtil.incr(recordTotalLikeNum+recordId, 1);
                        }
                        //返回点赞数
                        return Result.<Integer>builder().code(200).msg("SUCCESS").data(userNum.size()).build();
                    } finally {
                        //分布式锁释放
                        LockKeepLiveUtil.LIVE_TASK.remove(Thread.currentThread());
                        redisUtil.expire(changeLikeUserList+userId+recordId,-2, TimeUnit.SECONDS);
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //读锁释放
            LikeReadWriteLock.readLock.unlock();
        }
        return CommonResultBuilderUtil.commonError("服务器忙，请稍等");
    }


    @Override
    public Result<Record> putRecord(long moduleId, long userId, String recordContent, BufferedInputStream file, String filename) {
        if (recordContent != null) {
            filename = cdnUtil.uploadCommonFile(filename, file);
        }
        Record record = Record.builder()
                .moduleId(moduleId)
                .userId(userId)
                .recordImg(filename)
                .gmtCreate(LocalDateTime.now())
                .totalLike(0)
                .recordContent(recordContent)
                .build();
        record.setRecordId(recordMapper.insertRecord(record));
        return Result.<Record>builder().code(200).msg("SUCCESS").data(record).build();
    }

    @Override
    public Result<Record> putRecordWithoutImg(long moduleId, long userId, String recordContent) {
        return putRecord(moduleId, userId, recordContent, null, null);
    }

    @Override
    public Result<List<Record>> findRecordListByUserId(long userId, long moduleId, int currentPage) {
        int rows = 6;
        int start = (currentPage - 1) * rows;
        List<Record> recordList = recordMapper.getRecordListByUserId(userId, moduleId, start, rows);
        //若总点赞数有变化，需修改
        for (int i = 0; i < recordList.size(); i++) {
            Record record = recordList.get(i);
            Integer totalLike = (Integer) redisUtil.get(recordTotalLikeNum + record.getRecordId());
            if (totalLike != null) {
                record.setTotalLike(totalLike);
            }
            recordList.set(i, record);
        }
        return Result.<List<Record>>builder().code(200).msg("SUCCESS").data(recordList).build();
    }

    @Override
    public Result<?> putComment(Comment comment) {
        //设置发表时间
        comment.setGmtCreate(LocalDateTime.now());
        int num = recordMapper.insertComment(comment);
        if (num < 0) {
            return CommonResultBuilderUtil.commonError("提交失败");
        }
        return CommonResultBuilderUtil.commonSuccess("提交成功");
    }


    @Override
    public Result<RecordInfo> getRecordInfo(long recordId, long userId) {
        Record record = recordMapper.getRecordById(recordId);
        if (record == null) {
            return CommonResultBuilderUtil.commonError("网络异常");
        }
        RecordInfo recordInfo = new RecordInfo();
        int count = recordMapper.getUserHasLike(userId,recordId);
        recordInfo.setIsLike(count>0);
        //若总点赞数有变化，需修改
        Integer totalLike = (Integer) redisUtil.get(recordTotalLikeNum + record.getRecordId());
        if (totalLike != null) {
            record.setTotalLike(totalLike);
            final Set<Object> userList = redisUtil.zGet(recordUserLikeList + record.getRecordId());
            if(userList!=null) {
                recordInfo.setIsLike(userList.contains(userId+""));
            }
        }
        List<CommentInfo> comment = new LinkedList<>();
        //获取父评论集合
        List<UserCommentInfo> parentComment = recordMapper.getCommentList(recordId, 0L);
        System.out.println(parentComment);
        for (UserCommentInfo userComment : parentComment) {
            long pid = userComment.getComment().getCommentId();
            //获取父评论对应的子评论集合
            List<UserCommentInfo> childComment = recordMapper.getCommentList(recordId, pid);
            CommentInfo commentInfo = new CommentInfo();
            commentInfo.setParentComment(userComment);
            commentInfo.setChildComment(childComment);
            comment.add(commentInfo);
        }
        recordInfo.setRecord(record);
        recordInfo.setCommentInfo(comment);
        return Result.<RecordInfo>builder().code(200).msg("SUCCESS").data(recordInfo).build();
    }

    public void setRecordTotalLikeNum(String recordTotalLikeNum) {
        RecordServiceImpl.recordTotalLikeNum = recordTotalLikeNum;
    }

    public void setRecordUserLikeList(String recordUserLikeList) {
        RecordServiceImpl.recordUserLikeList = recordUserLikeList;
    }

    public void setNewUserRecord(String newUserRecord) {
        RecordServiceImpl.newUserRecord = newUserRecord;
    }

    public void setNewUserLikeList(String newUserLikeList) {
        RecordServiceImpl.newUserLikeList = newUserLikeList;
    }

    public void setDeleteUserRecord(String deleteUserRecord) {
        RecordServiceImpl.deleteUserRecord = deleteUserRecord;
    }

    public void setDeleteUserLikeList(String deleteUserLikeList) {
        RecordServiceImpl.deleteUserLikeList = deleteUserLikeList;
    }

    public void setChangeLikeUserList(String changeLikeUserList) {
        RecordServiceImpl.changeLikeUserList = changeLikeUserList;
    }

    public static String getRecordUserLikeList() {
        return recordUserLikeList;
    }

    public static String getNewUserRecord() {
        return newUserRecord;
    }

    public static String getNewUserLikeList() {
        return newUserLikeList;
    }

    public static String getRecordTotalLikeNum() {
        return recordTotalLikeNum;
    }

    public static String getDeleteUserRecord() {
        return deleteUserRecord;
    }

    public static String getDeleteUserLikeList() {
        return deleteUserLikeList;
    }
}
